﻿#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
//常属性
//const修饰变量的时候，叫：常变量
//这个被修饰的变量本质上还是变量，只是不能被修改
//
//int main()
//{
//	//int arr[10] = {0};
//	const int n = 10;
//	//C99 之前不支持变长数组，数组大小是需要常量、常量表达式来指定的，不能是变量
//	int arr[n];
//
//	return 0;
//}
//int main()
//{
//	int n = 10;
//	int m = 100;
//
//	int const * p = &n;
//	//const 修饰指针变量
//	//放在*的左边，限制的是指针指向的内容，也就是不能通过指针变量来修改它所指向的内容 
// 
// 
//	//但是指针变量本身是可以改变的
//
//	*p = 20;//err //*p 不能用
//	p = &m;//ok  //p 能用
//	return 0;
//}

//int main()
//{
//	int n = 10;
//	int m = 100;
//
//	int * const p = &n;
//	//const 修饰指针变量
//	//放在*的右边，限制的是指针变量本身，指针不能改变它的指向
//	//但是可以通过指针变量修改它所指向的内容
//
//	*p = 20;//ok
//	p = &m;//err
//
//	return 0;
//}
// 
// 
// ////指针和整数的减运算
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	//              0 1 2 3 4 5 6 7 8 9
//	int i = 0;
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int* p = &arr[0];//*p直接取到1的地址
//
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *p);
//		p++;//每次加一个整型的长度 一个整数int正好是四个字节 就可以加到数组的下一个地址 就可以输出1 2 3 4 5 6 7 8 9 10
//	}
//
//	return 0;
//}


//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; 
//	//              0 1 2 3 4 5 6 7 8 9
//	int i = 0;
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int* p = &arr[0];
//
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//
//	return 0;
//}
//



//指针和整数的减运算
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	//              0 1 2 3 4 5 6 7 8 9
//	int i = 0;
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int* p = &arr[sz-1];
//
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *p);
//		p--;
//	}
//
//	return 0;
//}

//指针减指针的运算
//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//	printf("%d\n", &arr[9] - &arr[0]);//指针减指针的绝对值是指针和指针之间的元素个数 = 9
//	//计算的前提条件是两个指针是指向的是用一个空间
//	return 0;
//}
//size_t my_strlen(char* p)
//{
//	char *start = p;
//	char *end = p;
//	while (*end)
//	{
//		end+;
//	}
//	return end - start;
//}
//int main()
//{
//	char arr[] = "abcdef";
//	size_t len = my_strlen(arr);//数组名就是首元素的地址 arr == &arr[0]
//	printf("%d\n", len);
//	return 0;
//}


////指针关系运算
//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int* p = &arr[0];
//	while(p < &arr[sz])
//	{
//		printf("%d ", *p);
//		p++;
//	}
//	return 0;
//}


//野指针 就是没有没有限制条件 没有指向的地址
//int main()
//{
//	//一个局部变量不初始化的话，它的只是随机的
//	int* p;//p是局部变量，但是没有初始化，其值是随机值的，如果将p中存放的值当做地址
// //解引用操作符就会形成非法访问
//	*p = 20;//p就是野指针
//
//	return 0;
//}

//assert 断⾔
//assert.h 头⽂件定义了宏 assert() ，⽤于在运⾏时确保程序符合指定条件，如果不符合，就报
//错终⽌运⾏。这个宏常常被称为“断⾔”。
//assert() 的缺点是，因为引⼊了额外的检查，增加了程序的运⾏时间。
//如果已经确认程序没有问
//题，不需要再做断⾔，就在 #include <assert.h> 语句的前⾯，定义⼀个宏 NDEBUG 。
//#define NDEBUG
//#include<assert.h>
//int main()
//{
//	int a = 100;
//	int* p = NULL;
//	assert(p != NULL);
//}


//strlen的模拟实现
#define NDEBUG
#include<assert.h>
size_t my_strlen(const char* s)
{
	int count = 0;
	assert(s!= NULL);
	while (*s)
	{
		count++;
		s++;
	}
	return  count;
}
int main()
{
	char arr[] = "abcdef";
	size_t len = my_strlen(arr);//数组名就是首元素的地址 arr == &arr[0]
	printf("%d\n", len);
	return 0;
}

//传值调⽤和传址调⽤

//传值调⽤
//void smap(int a, int b)
//{
//	int z = 0;
//	z = a;
//	a = b;
//	b = z;
//}
//int main()
//{
//	int a = 0;
//	int b = 0;
//	scanf("%d %d", &a, &b);
//	printf("交换前 a = %d b = %d\n", a, b);
//	smap(a, b); //传值调⽤
//	printf("交换后 a = %d b = %d", a, b);
//}

//传址调⽤
//void smap1(int *a, int *b)
//{
//	int z = 0;
//	z = *a;
//	*a = *b;
//	*b = z;
//}
//int main()
//{
//	int a = 0;
//	int b = 0;
//	scanf("%d %d", &a, &b);
//	printf("交换前 a = %d b = %d\n", a, b);
//	smap1(&a, &b); //传址调⽤
//	printf("交换后 a = %d b = %d", a, b);
//}
//传址调⽤，可以让函数和主调函数之间建⽴真正的联系，在函数内部可以修改主调函数中的变量；所
//以未来函数中只是需要主调函数中的变量值来实现计算，就可以采⽤传值调⽤。如果函数内部要修改
//主调函数中的变量的值，就需要传址调⽤。